import { FunctionalComponent, UnwrapRef } from "vue";
import { FormItemType, FormMethods } from "./types";
import { Col, Form, FormInstance, FormItem, Row } from "@arco-design/web-vue";
import { get, isFunction, isNil, omit, set } from "lodash-es";
import components from "./components";

type UseFormReturn<T> = [
  FunctionalComponent<Omit<FormInstance["$props"], "model">>,
  {
    setFormValue: (data: Nullable<UnwrapRef<T>>) => void;
    getFormValue: () => UnwrapRef<T>;
  } & Omit<FormMethods, "getFormValue" | "setFormValue" | "updateFormItem">
];

export default function useForm<T extends Recordable = {}>(
  formItemsConfig: FormItemType[] | ((model: Ref<any>) => FormItemType[])
): UseFormReturn<T> {
  const formRef = ref<Nullable<FormMethods>>(null);
  const formModel = ref<T>({} as T);
  const formItems = isFunction(formItemsConfig) ? formItemsConfig(formModel) : formItemsConfig;

  const setFormValue = (data: Nullable<UnwrapRef<T>>) => {
    if (data) formModel.value = data;
    else if (isNil(data)) formModel.value = {} as UnwrapRef<T>;
  };

  const getFormValue = () => {
    return unref(formModel)!;
  };

  const validate: FormMethods["validate"] = (callback) => {
    return unref(formRef)?.validate(callback)!;
  };

  const validateField: FormMethods["validateField"] = (field, callback) => {
    return unref(formRef)?.validateField(field, callback)!;
  };

  const resetField: FormMethods["resetField"] = (field) => {
    unref(formRef)?.resetField(field);
  };

  const clearValidate: FormMethods["clearValidate"] = (field) => {
    unref(formRef)?.clearValidate(field);
  };

  const setFields: FormMethods["setFields"] = (data) => {
    unref(formRef)?.setFields(data);
  };

  const scrollToField: FormMethods["scrollToField"] = (field) => {
    unref(formRef)?.scrollToField(field);
  };

  const CustomForm: FunctionalComponent<Omit<FormInstance["$props"], "model">> = (props) => {
    const createPlaceholer = (item: FormItemType) => {
      if (["Input", "InputPassword", "InputNumber", "Textarea"].includes(item.component ?? "")) {
        return `请输入${item.label}`;
      } else if (["Select", "InputTag"].includes(item.component ?? "")) {
        return `请选择${item.label}`;
      } else {
        return undefined;
      }
    };
    const comp = (item: FormItemType) => {
      if (item.component) {
        const Comp = components[item.component!] as any;
        const compProps = {
          modelValue: get(formModel.value, item.field!),
          "onUpdate:modelValue": (value: any) => set(formModel.value!, item.field!, value),
          options: item.options,
          placeholder: createPlaceholer(item),
          ...item.props,
        };
        return (
          <Comp
            {...compProps}
            modelValue={get(formModel.value, item.field!)}
            onUpdate:modelValue={(value: any) => set(formModel.value!, item.field!, value)}
          />
        );
      } else if (item.render) {
        const Comp = item.render(formModel) as any;
        return <Comp />;
      } else {
        return <>{get(unref(formModel), item.field!)}</>;
      }
    };
    return (
      <>
        <Form ref={formRef} model={formModel.value ?? {}} {...props} autoLabelWidth>
          <Row gutter={32}>
            {formItems.map((item) => {
              return (
                <Col span={item.col ?? 24}>
                  <FormItem {...omit(item, "component", "props", "options", "col")}>
                    {comp(item)}
                  </FormItem>
                </Col>
              );
            })}
          </Row>
        </Form>
      </>
    );
  };

  return [
    CustomForm,
    {
      setFormValue,
      getFormValue,
      validate,
      validateField,
      resetField,
      clearValidate,
      setFields,
      scrollToField,
    },
  ];
}
